package physicsEngine;
import java.awt.*;
import java.awt.event.*;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.nio.*;
import java.util.*;

import javax.media.opengl.*;
import javax.media.opengl.awt.*;
import javax.media.opengl.glu.*;
import javax.swing.*;

import com.jogamp.common.nio.*;
import com.jogamp.opengl.util.*;

public class PhysicsScene implements GLEventListener, ActionListener, KeyListener {
	private static final int TARGET_FPS = 60;
	
	private Peer peer;
	private ArrayList<SceneGraphNode> selected = new ArrayList<SceneGraphNode>();
	PhysicsObject objSelected = null;
	private static JFrame appFrame;
	static SceneGraphNode sceneGraphRoot;
	private static boolean pickNextFrame;
	private static Point pickedPoint;
	private static double left, right, top, bottom;
	private static ArrayList<PhysicsObject> objects;
	int lives = 5;
	int score = 0;
	final JLabel playerTitle = new JLabel("Player lives: " + lives);
	JLabel playerScore = new JLabel("Score: " + score);
	
	
	
	JLabel playerOnePoints = new JLabel();
	
	
	final JMenuBar menuBar = new JMenuBar();
	final JMenu addMenu = new JMenu("Add");
	final JLabel filler = new JLabel("");
	final JMenuItem buildRectangle = new JMenuItem("Rectangle");
	final JMenuItem buildCircle = new JMenuItem("Circle");
	final JMenuItem buildTriangle = new JMenuItem("Triangle");
	final JButton deleteObject = new JButton("Delete");
	final PhysicsObject paddle = new PaddleBlock(1f);
	
	boolean leftPressed;
	boolean rightPressed;
	float paddleStart;
	int mouseStart;
	int mouseEnd;
	
	public static void main(String[] args) {
		GLProfile.initSingleton();
		SwingUtilities.invokeLater(new Runnable() {
			public void run() {
				(new PhysicsScene()).createAndShowGUI();
			}
		});
	}

	private void handlePeerUI() {
		BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
		boolean newNetwork = false;
		String input = null;

		while (true) {
			System.out.println("Create a new network or join an existing network?");
			System.out.println("  1. Create a new network");
			System.out.println("  2. Join an existing network");
			try {
				input = in.readLine();
			} catch (IOException e) {
				continue;
			}
			if (input.equals("1")) {
				newNetwork = true;
				break;
			} else if (input.equals("2"))
				break;
		}
		long id = -1;
		while (true) {
			System.out.println("Desired network ID, 0-" + (Peer.ID_LIMIT-1) + " [random]: ");
			try {
				input = in.readLine();
				if (input.length() == 0)
					break;
				id = Long.parseLong(input);
			} catch (NumberFormatException e) {
				continue;
			} catch (IOException e) {
				continue;
			}
			if (id >= -1 && id < Peer.ID_LIMIT)
				break;
		}
		peer = new Peer(this);
		if (newNetwork) {
			if (id == -1)
				peer.createNetwork();
			else
				peer.createNetwork(id);
		} else {
			String ip = "127.0.0.1";
			int port = Peer.DEFAULT_SERVER_PORT;
			do {
				System.out.print("Enter host to connect to [" + ip + "[:" + port + "]]: ");
				try {
					input = in.readLine();
					int index = input.indexOf(':');
					if (index >= 0) {
						port = Integer.parseInt(input.substring(index + 1));
						input = input.substring(0, index);
					}
					if (input.length() > 0)
						ip = input;
				} catch (IOException e) {
				}
			} while (id == -1 ? !peer.connectToNetwork(ip, port) : !peer.connectToNetwork(ip, port, id));
		}

	}
	
	private void createAndShowGUI() {
		// Fix for background flickering
		// System.setProperty("sun.awt.noerasebackground", "true");
		handlePeerUI();
		
		
		GLProfile profile = GLProfile.getDefault();
		GLCapabilities capabilities = new GLCapabilities(profile);
		GLCanvas canvas = new GLCanvas(capabilities);
		canvas.addGLEventListener(this);

		canvas.addKeyListener(this);

	
		canvas.addMouseListener(new MouseAdapter() {
			public void mouseClicked(MouseEvent e) {
				pickNextFrame = true;
				pickedPoint = new Point(e.getX(), e.getY());
			}
			
		});

		objects = new ArrayList<PhysicsObject>();
		sceneGraphRoot = new SceneGraphNode(false);
		
		
		paddle.position.x = -1;
		paddle.position.y = -4;
		float xpos = -4.5f;
		float ypos = 4.25f;
		attachObject(paddle);
		for (int i = 0; i < 6; i ++) {
			for (int j = 0; j < 16; j++) {
				PhysicsObject bricks = new Rectangle(.25f);
				xpos += .5f;
				
				bricks.position.x = xpos;
				bricks.position.y = ypos;
				attachObject(bricks);
			}
			xpos = -4.5f;
			ypos -= .25f;
		}
		
		
		attachObject(new HalfSpace(new Vector2f(-5, 0), new Vector2f(1, 0)));
		attachObject(new HalfSpace(new Vector2f(0, -5), new Vector2f(0, 1)));//(this is the bottom halfspace
		attachObject(new HalfSpace(new Vector2f(5, 0), new Vector2f(-1, 0))); //(this is the right halfspace
		attachObject(new HalfSpace(new Vector2f(0, 5), new Vector2f(0, -1)));
		// Add independent SceneGraphNode representing all the HalfSpaces.
		sceneGraphRoot.addChild(new SceneGraphNode(false) {
			public void renderGeometry(GLAutoDrawable drawable) {
				GL2 gl = drawable.getGL().getGL2();	
				gl.glColor3f(1, 1, 1);
				gl.glBegin(GL.GL_LINE_LOOP);
				gl.glVertex2f(-5, -5);
				gl.glVertex2f(5, -5);
				gl.glVertex2f(5, 5);
				gl.glVertex2f(-5, 5);
				gl.glEnd();
			}
		});
		
		
		appFrame = new JFrame("Physics Scene");
		JPanel border = new JPanel(new BorderLayout());
		JPanel northPanel = new JPanel(new GridLayout());
		JPanel optionsPanel = new JPanel(new GridLayout(10, 2));
	
		
		
		
		optionsPanel.add(playerTitle);
		optionsPanel.add(playerScore);
		
		northPanel.add(menuBar);
		
		buildCircle.addActionListener(this);
		buildTriangle.addActionListener(this);
		buildRectangle.addActionListener(this);
		
		menuBar.add(addMenu);
		addMenu.add(buildCircle);
		
		
		border.add(canvas, BorderLayout.CENTER);
		border.add(northPanel, BorderLayout.NORTH);
		border.add(optionsPanel, BorderLayout.EAST);
		appFrame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
		appFrame.setMinimumSize(new Dimension(256, 256));
		appFrame.add(border);
		appFrame.pack();
		if (Toolkit.getDefaultToolkit().isFrameStateSupported(JFrame.MAXIMIZED_BOTH))
			appFrame.setExtendedState(appFrame.getExtendedState() | JFrame.MAXIMIZED_BOTH);
		appFrame.setVisible(true);
	}

	public void attachObject(PhysicsObject object) {
		if (object.renderable != null)
			sceneGraphRoot.addChild(object.renderable);
		objects.add(object);
	}

	public void detachObject(PhysicsObject object) {
		if (object.renderable != null)
			sceneGraphRoot.removeChild(object.renderable);
		int index = objects.indexOf(object);
		if (index == -1)
			throw new IllegalArgumentException();
		objects.set(index, objects.get(objects.size()-1));
		objects.remove(objects.size()-1);
		objSelected = null;
	}

	public void display(GLAutoDrawable drawable) {
		GL2 gl = drawable.getGL().getGL2();
		gl.glClear(GL.GL_COLOR_BUFFER_BIT);
		if (pickNextFrame) {
			GLU glu = GLU.createGLU(gl);
			int viewport[] = new int[4];
			gl.glGetIntegerv(GL.GL_VIEWPORT, viewport, 0);
			gl.glMatrixMode(GL2.GL_PROJECTION);
			gl.glPushMatrix();
			gl.glLoadIdentity();
			glu.gluPickMatrix(pickedPoint.x, (double)(viewport[3] - pickedPoint.y), 1, 1, viewport, 0);
			gl.glOrtho(left, right, bottom, top, -1, 1);
			gl.glMatrixMode(GL2.GL_MODELVIEW);
			System.out.println(sceneGraphRoot.getPicked(drawable));
			ArrayList<SceneGraphNode> temp =  sceneGraphRoot.getPicked(drawable);
			selected.clear();
			for (SceneGraphNode node: temp) {
				selected.add(node);
			}
			getSelectedObject();
			gl.glMatrixMode(GL2.GL_PROJECTION);
			gl.glPopMatrix();
			gl.glMatrixMode(GL2.GL_MODELVIEW);
			pickNextFrame = false;
		}
		for (PhysicsObject object : objects)
			object.updateState(1f / TARGET_FPS);
		boolean noCollisions = false;
		for (int repeat = 0; repeat < 10 && !noCollisions; repeat++) {
			noCollisions = true;		
			for (int i = 0; i < objects.size(); i++) {
				PhysicsObject a = objects.get(i);
				for (int j = i + 1; j < objects.size(); j++) {
					PhysicsObject b = objects.get(j);
					CollisionInfo cInfo = a.getCollision(b);
					if (cInfo != null) {
						noCollisions = false;
						a.resolveCollision(b, cInfo);
						if (a instanceof Rectangle) {
							score += 10;
							playerScore.setText("Score: " + score);
							detachObject(a);
						} else if (b instanceof Rectangle) {
							score += 10;
							playerScore.setText("Score: " + score);
							detachObject(b);
						}
						if (cInfo.leftHalf) {
							if (a instanceof Circle) {
								peer.sendText(a, false);
							} else {
								peer.sendText(b, false);
							}
						} else if (cInfo.rightHalf) {
							if (a instanceof Circle) {
								peer.sendText(a, true);
							} else {
								peer.sendText(b, true);
							}
						} else if (cInfo.dead) {
							if (a instanceof Circle) {
								lives -= 1;
								playerTitle.setText("Player lives: " + lives);
								detachObject(a);
							} else {
								lives -= 1;
								playerTitle.setText("Player lives: " + lives);
								detachObject(b);
							}
							
						}
					}
				}
			}
			if (!(repeat+1 < 10 && !noCollisions)){
				//System.out.println(repeat + 1);
			}
		}
		for (PhysicsObject object : objects)
			object.updateRenderable();
		sceneGraphRoot.render(drawable);
	}

	public void dispose(GLAutoDrawable drawable) {
	}

	private void getSelectedObject() {
		if (objects != null) {
			for (PhysicsObject obj: objects) {
				if (obj.renderable != null && selected.size() != 0 && obj.renderable == selected.get(selected.size() - 1)) {
					objSelected = obj;
				}
			}
		}
	}
	
	public void init(GLAutoDrawable drawable) {
		GL2 gl = drawable.getGL().getGL2();
		gl.glClearColor(0, 0, 0, 0);		
		IntBuffer selectBuffer = Buffers.newDirectIntBuffer(3);
		gl.glSelectBuffer(selectBuffer.capacity(), selectBuffer);
		FPSAnimator fps = new FPSAnimator(drawable, TARGET_FPS);
		fps.start();
	}

	public void reshape(GLAutoDrawable drawable, int x, int y, int width, int height) {
		final double UNIT = 11;
		
		if (width < height) {
			left = -UNIT / 2;
			right = UNIT / 2;
			top = (double)height / width * UNIT / 2;
			bottom = -top;
		} else {
			top = UNIT / 2;
			bottom = -UNIT / 2;
			right = (double)width / height * UNIT / 2;
			left = -right;
		}
		
		GL2 gl = drawable.getGL().getGL2();
		gl.glMatrixMode(GL2.GL_PROJECTION);
		gl.glLoadIdentity();
		gl.glOrtho(left, right, bottom, top, -1, 1);
		gl.glMatrixMode(GL2.GL_MODELVIEW);
	}

	@Override
	public void actionPerformed(ActionEvent event) {
		if (event.getSource() == buildCircle) {
			PhysicsObject obj = new Circle(.15f);
			obj.position.x = 0;
			obj.position.y = 0;
			obj.acceleration.y = 0;
			obj.velocity.y = -10;
			obj.velocity.x = 2;
			attachObject(obj);
		} 
	}

	@Override
	public void keyPressed(KeyEvent event) {
		// TODO Auto-generated method stub
		if (event.getKeyCode() == KeyEvent.VK_RIGHT)
        {
			float x = paddle.position.x;
			if (x  < 3.85f) {
				paddle.velocity.x = 5f;
			} else {
        		paddle.velocity.x = 0;
        	}
          
        }else if (event.getKeyCode() == KeyEvent.VK_LEFT){
        	if (paddle.position.x > -5f) {
        		paddle.velocity.x = -5f;
        	} else {
        		paddle.velocity.x = 0;
        	}
        } 
	}

	

	@Override
	public void keyReleased(KeyEvent e) {
		paddle.velocity.x = 0;
		
	}

	@Override
	public void keyTyped(KeyEvent e) {
		// TODO Auto-generated method stub
		
	}
}


